<?php
/**
 *+------------------
 * madong
 *+------------------
 * Copyright (c) https://gitee.com/motion-code  All rights reserved.
 *+------------------
 * Author: Mr. April (405784684@qq.com)
 *+------------------
 * Official Website: http://www.madong.tech
 */

namespace madong\laravel\wf\services;

use madong\laravel\wf\basic\BaseService;
use madong\ingenious\enums\ProcessConstEnum;
use madong\ingenious\ex\LFlowException;
use madong\ingenious\interface\model\IProcessDesign;
use madong\ingenious\interface\services\IProcessDesignService;
use madong\ingenious\libs\utils\ArrayHelper;
use madong\ingenious\libs\utils\AssertHelper;
use madong\ingenious\libs\utils\Pagination;
use madong\ingenious\libs\utils\PropertyCopier;
use madong\laravel\wf\dao\ProcessDesignDao;

class ProcessDesignService extends BaseService implements IProcessDesignService
{

    public function __construct()
    {
        $this->dao = new ProcessDesignDao();
    }

    public function created(object $param): ?IProcessDesign
    {
        unset($param->id);
        AssertHelper::notTrue($this->checkUniqueName(['name' => $param->name]), '唯一编码已存在，请检查name参数');;
        $model = $this->dao->getModel();
        PropertyCopier::copyProperties($param, $model);
        return $this->dao->save($model->toArray());
    }

    public function updated(object $param): bool
    {
        AssertHelper::notNull($param->id ?? '', '参数异常，请检查ID参数');
        AssertHelper::notNull($param->name ?? '', '参数异常，唯一编码不能为空');
        AssertHelper::notTrue($this->checkUniqueName(['notId' => $param->id, 'name' => $param->name]), '唯一编码已存在，请检查name参数');
        $model = $this->dao->getModel()->where('id', $param->id)->first();
        AssertHelper::notNull($model, '资源不存在');
        PropertyCopier::copyProperties($param, $model);
        return $model->save();
    }

    public function del(string|array|int $data): array
    {
        return $this->transaction(function () use ($data): array {
            $data       = is_array($data) ? $data : explode(',', $data);
            $deletedIds = [];
            foreach ($data as $id) {
                $item = $this->dao->get($id, ['*'], ['history']);
                if (!$item) {
                    continue; // 如果找不到项，跳过
                }
                $item->history()->delete();//删除关联历史记录
                $item->delete();//删除主表
                $primaryKey   = $item->getPk();
                $deletedIds[] = $item->{$primaryKey};
            }
            return $deletedIds;
        });
    }

    public function list(object $param): array
    {
        $where = ArrayHelper::paramsFilter($param, [
            ['type_id', ''],
            ['name', ''],
            ['enabled', 1],
            ['pid', 0],
        ]);
        [$page, $limit] = Pagination::getPageValue($param);
        $items = $this->dao->selectList($where, '*', $page, $limit, 'name asc', ['types'], true);
        $total = $this->dao->count($where, true);
        return compact('items', 'total');
    }

    public function findById(string $id): ?IProcessDesign
    {
        AssertHelper::notNull($id, '参数ID不能为空');
        $record = parent::get($id, ['*'], ['types', 'history']);
        if ($record && $record->history) {
            $latestHistory          = $record->history->sortByDesc('create_time')->first();
            $record->latest_history = $latestHistory; // 将最新的历史记录添加到模型中
        }
        return $record;
    }

    public function updateDefine(object $jsonObject): bool
    {
        return $this->transaction(function () use ($jsonObject) {
            try {
                $data                    = new \stdClass();
                $data->process_design_id = $jsonObject->{ProcessConstEnum::PROCESS_DESIGN_ID_KEY->value};
                $data->create_user       = $jsonObject->{ProcessConstEnum::CREATE_USER->value};
                $processDesign           = $this->dao->get($data->process_design_id);
                $processDesign->set('update_time', time());
                $processDesign->set('is_deployed', 0);
                $processDesign->set('update_user', $data->create_user);
                $processDesign->save();
                unset($jsonObject->{ProcessConstEnum::CREATE_USER->value});
                unset($jsonObject->{ProcessConstEnum::PROCESS_DESIGN_ID_KEY->value});
                $data->content = $jsonObject;
                // 获取当前版本号并转换为浮点数
                $processDesignHistoryService = new ProcessDesignHistoryService();
                $historyModel                = $processDesignHistoryService->dao->getModel();
                $newVersion                  = 1.0;//默认版本
                $history                     = $processDesignHistoryService->dao->selectList(['process_design_id' => $data->process_design_id], 'version', 0, 0, 'version asc', [], true)->last();
                if (!empty($history)) {
                    $currentVersion = (float)$history->getData('version');
                    $newVersion     = round($currentVersion + 0.1, 1);
                }
                $data->versions    = $newVersion;
                $data->create_time = time();
                PropertyCopier::copyProperties($data, $historyModel);
                return $historyModel->save();
            } catch (\Throwable $e) {
                 throw new LFlowException($e->getMessage());
            }
        });
    }

    public function deploy(string|int $processDesignId, string|int $operation): bool
    {
        try {
            return $this->transaction(function () use ($processDesignId, $operation) {
                try {
                    $model = $this->dao->get($processDesignId);
                    AssertHelper::notNull($model, '部署失败，流程设计不存在或被删除');
                    AssertHelper::notNull($operation, 'operation 不能为空');
                    $model->set('is_deployed', 1);
                    $model->set('update_user', $operation);
                    $latestHistory = $model->history->sortByDesc('create_time')->first();
                    AssertHelper::notNull($latestHistory, '流程设计不能为空');
                    $model->save();
                    $processDefineService = new  ProcessDefineService();
                    $content              = json_decode($latestHistory->getData('content'), true);
                    AssertHelper::notNull($content, '请先设计流程');
                    $content['version'] = $latestHistory->getData('version');//追加版本
                    return $processDefineService->deploy(ArrayHelper::arrayToObject($content), $operation);
                } catch (\Throwable $e) {
                    throw new LFlowException($e->getMessage());
                }
            });
        } catch (\Throwable $e) {
            throw new LFlowException($e->getMessage());
        }
    }

    public function redeploy(string $processDesignId, string|int $operation): bool
    {

        return $this->transaction(function () use ($processDesignId, $operation) {
            try {
                $model = $this->dao->get($processDesignId);
                AssertHelper::notNull($model, '部署失败，流程设计不存在或被删除');
                AssertHelper::notNull($operation, 'operation 不能为空');
                $model->set('is_deployed', 1);
                $model->set('update_user', $operation);
                $latestHistory = $model->history->sortByDesc('create_time')->first();
                AssertHelper::notNull($latestHistory, '流程设计不能为空');
                $model->save();
                $processDefineService = new  ProcessDefineService();
                $content              = json_decode($latestHistory->getData('content'), true);
                AssertHelper::notNull($content, '请先设计流程');
                $lastDefineModel = $processDefineService->dao->selectList(['name' => $model->getData('name')], '*', 0, 0, 'version', [], true)->last();
                AssertHelper::notNull($lastDefineModel, '该流程未部署,请先部署流程');
                return $processDefineService->redeploy($lastDefineModel->getData('id'), ArrayHelper::arrayToObject($content), $operation);
            } catch (\Throwable $e) {
                throw new LFlowException($e->getMessage());
            }
        });
    }

    private function checkUniqueName(array $where): bool
    {
        $result = $this->dao->selectList($where, '*', 0, 0, '', [], true)->toArray();
        return !empty($result) ?? false;
    }

}
